#pragma once
#include <string>
#include <fstream>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable:4996)

using namespace std;
class TcpSocket
{
public:
    TcpSocket(); // version config
    ~TcpSocket(); // close socket
    SOCKET m_Scoket; // socket object
    string RecvMsg; // recv info
    bool SocketInit(); // socket init
    bool CreatSocket(); // create socket
    bool SendMsg(string strSendBuf, string& strRecvMsg); // send msg -- strSendBuf: send msg body , strRecvMsg: reply after sending 
    void StartReadRecvThread(); // open thread keep alive and listen server msg
 
private:
    DWORD m_dwVserion; // version
    WSADATA m_WsData; 
    int m_iError; // crate, init or connect error
    bool m_isSocketFlag; // socket connect status 
    string CONF_PATH = "./tcpConf.txt"; // socket config file
    string ip_address; // socket ip address
    int port; // socket port
};

TcpSocket::TcpSocket()
{
    m_dwVserion = 0;
    m_isSocketFlag = true;
    memset(&m_WsData, 0, sizeof(WSADATA));
}

TcpSocket::~TcpSocket()
{
    closesocket(m_Scoket);
    WSACleanup();
}

bool TcpSocket::SocketInit()
{
    m_dwVserion = MAKEWORD(1, 1);
    m_iError = WSAStartup(m_dwVserion, &m_WsData);
    if (m_iError != 0)
    {
        printf("socket init failed!\n");
        m_isSocketFlag = false;
    }
    else 
    {
        printf("socket init success!\n");
    }
    if (LOBYTE(m_WsData.wVersion) != 1 && HIBYTE(m_WsData.wVersion) != 1)
    {
        WSACleanup();
        m_isSocketFlag = false;
    }

    // read ip and port config
    std::ifstream config;
    config.open(CONF_PATH.c_str());
    
    if(config.is_open()){
        char tmp[100];
        while (!config.eof())
        {
            config.getline(tmp, 100);
            string line(tmp);
            size_t pos = line.find(':');
            string tmpKey = line.substr(0, pos);
            if(tmpKey == "ip_address")
            {
                ip_address = line.substr(pos + 1);
            }
            else if(tmpKey == "port")
            {
                port = stoi(line.substr(pos + 1));
            }
        }
    }
    return m_isSocketFlag;
}

bool TcpSocket::CreatSocket()
{
    //create socket
    m_Scoket = socket(AF_INET, SOCK_STREAM, 0);
    SOCKADDR_IN addr;
    addr.sin_addr.S_un.S_addr = inet_addr(ip_address.c_str());
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);

    m_iError = connect(m_Scoket, (SOCKADDR*)&addr, sizeof(SOCKADDR));
    if(m_iError != 0)
    {
        printf("socket connect fail!\n");
        m_isSocketFlag = false;
    }
    else{
        m_isSocketFlag = true;
        printf("socket connected!\n");
    }
    return m_isSocketFlag;
}

bool TcpSocket::SendMsg(string strSendBuf, string& strRecvMsg)
{
    if (!m_isSocketFlag)
        return false;
    printf("send msg start!\n");
    // send msg
    int iRet = send(m_Scoket, strSendBuf.c_str(), strSendBuf.length(), 0);
    
    if (iRet == 0)
    {
        printf("send timeout!\n");
        return false;
    }
    printf("send msg: %s\n", strSendBuf.c_str());

    // recv msg
    char recvBuf[1024] = { 0 };
    iRet = recv(m_Scoket, recvBuf, sizeof(recvBuf), 0);
    if (iRet == -1)
    {
        printf("recv timeout!\n");
        return false;
    }
    strRecvMsg = string(recvBuf);
    return true;
}

DWORD WINAPI ReadRecv(LPVOID lpParam)  // keepalive and recv server msg
{
    while (1)
    {
        TcpSocket *readData = (TcpSocket*)(lpParam);
        char recvBuf[1024] = { 0 };
        int iRet = recv(readData -> m_Scoket, recvBuf, sizeof(recvBuf), 0);
        if (iRet > 0) {
            readData -> RecvMsg = string(recvBuf);
            printf("listen for msg from the server: %s\n", readData -> RecvMsg.c_str());
        }
        else
        {
            readData -> CreatSocket();
            Sleep(1000);
            continue;
        }
        Sleep(1000);
    }
}


void TcpSocket::StartReadRecvThread() {
    HANDLE h = CreateThread(NULL, 0, ReadRecv, (LPVOID)this, 0, NULL);
}

